//
// Created by syj on 2023/10/12.
//

#include <iostream>
#include <string>

#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <fastdds/dds/publisher/DataWriterListener.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/subscriber/DataReader.hpp>
#include <fastdds/dds/subscriber/DataReaderListener.hpp>
#include <fastdds/dds/subscriber/SampleInfo.hpp>
#include <fastdds/dds/subscriber/qos/DataReaderQos.hpp>
#include <fastdds/dds/topic/Topic.hpp>

#include <fastrtps/attributes/ParticipantAttributes.h>
#include <fastrtps/Domain.h>
#include "MessagePubSubTypes.h"
#include "logger.h"

#include "MessagePublisher.h"

using namespace eprosima::fastdds::dds;
using namespace eprosima::fastrtps::rtps;

//Enums and configuration structure
enum Reliability_type
{
    Best_Effort, Reliable
};
enum Durability_type
{
    Transient_Local, Volatile
};
enum HistoryKind_type
{
    Keep_Last, Keep_All
};
enum Key_type
{
    No_Key, With_Key
};

typedef struct
{
    Reliability_type reliability;
    Durability_type durability;
    HistoryKind_type historykind;
    Key_type keys;
    uint16_t history_size;
    uint8_t depth;
    uint8_t no_keys;
    uint16_t max_samples_per_key;
} example_configuration;

void pastsamples();

int main()
{
#if 0
    pastsamples();
#else
    HelloWorldPublisher publisher;
    HelloWorld message;
    if (publisher.init())
    {
        message.index(1);
        message.message("Test");
        publisher.WriterMessage("samplePubSubTopic", message);
//        publisher.run();
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(5000));

#endif
    return 0;
}

void pastsamples()
{

    std::shared_ptr<HelloWorldPubSubType> helloWorldPubSubType{new HelloWorldPubSubType()};
    TypeSupport sampleType(helloWorldPubSubType.get());
    HelloWorld my_sample;
    SampleInfo sample_info;

    //Create Publisher Participant
#if 1
    DomainParticipantQos pqos;
    pqos.wire_protocol().builtin.discovery_config.leaseDuration = eprosima::fastrtps::c_TimeInfinite;
    pqos.name("PublisherParticipant");
    auto PubParticipant = DomainParticipantFactory::get_instance()->create_participant(0, pqos);

    if (PubParticipant == nullptr)
    {
        std::cout << " Something went wrong while creating the Publisher Participant..." << std::endl;
        return;
    }

    //Register the type
    sampleType.register_type(PubParticipant);

    //Create Publisher
    Publisher* myPub = PubParticipant->create_publisher(PUBLISHER_QOS_DEFAULT);

#else
    eprosima::fastrtps::ParticipantAttributes PParam;
    PParam.rtps.builtin.discovery_config.discoveryProtocol = DiscoveryProtocol_t::SIMPLE;
    PParam.rtps.builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = true;
    PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter = true;
    PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader = true;
    PParam.rtps.builtin.discovery_config.leaseDuration = {0,0};
    PParam.rtps.setName("Participant_pub");
    auto PubParticipant = eprosima::fastrtps::Domain::createParticipant(PParam);

    if (PubParticipant == nullptr)
    {
        std::cout << " Something went wrong while creating the Publisher Participant..." << std::endl;
        return;
    }

    //REGISTER THE TYPE

    eprosima::fastrtps::Domain::registerType(PubParticipant, helloWorldPubSubType.get());

    //CREATE THE PUBLISHER
    eprosima::fastrtps::PublisherAttributes Wparam;
    Wparam.topic.topicKind = NO_KEY;
    Wparam.topic.topicDataType = "HelloWorld";
    Wparam.topic.topicName = "HelloWorldTopic";
    Wparam.topic.historyQos.kind = KEEP_LAST_HISTORY_QOS;
    Wparam.topic.historyQos.depth = 30;
    Wparam.topic.resourceLimitsQos.max_samples = 50;
    Wparam.topic.resourceLimitsQos.allocated_samples = 20;
    Wparam.times.heartbeatPeriod.seconds = 0;
    Wparam.times.heartbeatPeriod.nanosec = 10 * 1000;
    Wparam.qos.m_reliability.kind = RELIABLE_RELIABILITY_QOS;
    auto myPub = eprosima::fastrtps::Domain::createPublisher(PubParticipant, Wparam, nullptr);
#endif

    if (myPub == nullptr)
    {
        std::cout << "Something went wrong while creating the Publisher..." << std::endl;
        return;
    }

    //Create Topic
    Topic* myTopic = PubParticipant->create_topic("samplePubSubTopic", sampleType.get_type_name(), TOPIC_QOS_DEFAULT);

    if (myTopic == nullptr)
    {
        std::cout << "Something went wrong while creating the Topic..." << std::endl;
        return;
    }

    //Create DataWriter
    DataWriterQos wqos;
    wqos.endpoint().history_memory_policy = DYNAMIC_RESERVE_MEMORY_MODE;
//    wqos.history().kind = KEEP_ALL_HISTORY_QOS;
    wqos.history().kind = KEEP_LAST_HISTORY_QOS;
    wqos.durability().kind = TRANSIENT_LOCAL_DURABILITY_QOS;
    wqos.reliability().kind = RELIABLE_RELIABILITY_QOS;
    wqos.history().depth =  50;
    wqos.resource_limits().max_samples = 100;
    wqos.resource_limits().max_instances = 1;
    wqos.resource_limits().max_samples_per_instance = 100;

    DataWriter* myWriter = myPub->create_datawriter(myTopic, wqos);

    std::cout << "Creating Publisher..." << std::endl;

    //Send 20 samples
    Logger_Info("Publishing 20 samples on the topic...");
//    for (uint8_t j = 0; j < 20; j++)
    {
        my_sample.index(1);
        my_sample.message("Test");
        myWriter->write(&my_sample);
    }

//    std::this_thread::sleep_for(std::chrono::milliseconds(1));

    std::cout << "Deleting Publisher" << std::endl;

    myPub->delete_datawriter(myWriter);
    PubParticipant->delete_publisher(myPub);
    PubParticipant->delete_topic(myTopic);
    DomainParticipantFactory::get_instance()->delete_participant(PubParticipant);
}